﻿
Imports System.Net.Sockets
Imports System.Threading
Imports std
Imports lock_t = lock(Of locks.monitorlock)

Public Class tcp_pac_server(Of ASYNC_OPERATION As _boolean)
    Private Shared ReadOnly async As Boolean
    Public Event receive_async(ByVal input() As Byte, ByVal output As pointer(Of bytes), ByRef ec As eventComb)
    Public Event receive_sync(ByVal input() As Byte, ByRef output() As Byte, ByRef result As Boolean)
    Public maxConnectionCount As Int64 = 128
    Private ReadOnly _port As Int32
    Private ReadOnly listener As tcpListener2 = Nothing
    Private ReadOnly [handles] As vector(Of handle) = Nothing
    Private l As lock_t
    Private stopping As Boolean = False

    Shared Sub New()
        async = alloc(Of ASYNC_OPERATION)()
    End Sub

    Public Function connectionCount() As Int64
        Return [handles].size()
    End Function

    Public Function active() As Boolean
        Return listener.Active()
    End Function

    Public Sub New(ByVal port As Int32)
        assert(port <> 0)
        [handles] = New vector(Of handle)()
        listener = New tcpListener2(port)
        socketTrans.initial(listener.Server())
        Try
            listener.Start()
        Catch ex As Exception
            raiseError(errorHandle.errorType.warning, "failed to start listener, ex ", ex.Message)
        End Try
        listen()
    End Sub

    Private Sub add_handle(ByVal h As handle)
        assert(Not h Is Nothing)
        l.wait()
        Dim i As Int64 = 0
        For i = 0 To [handles].size() - 1
            If [handles](i) Is Nothing Then
                [handles](i) = h
                Exit For
            End If
        Next
        If i = [handles].size() Then
            [handles].push_back(h)
        End If
        l.release()
    End Sub

    Private Sub remove_handle(ByVal h As handle)
        assert(Not h Is Nothing)
        Dim i As Int64 = 0
        l.wait()
        For i = 0 To [handles].size() - 1
            If object_compare(h, [handles](i)) = 0 Then
                [handles](i) = Nothing
                Exit For
            End If
        Next
        l.release()
        assert(i < [handles].size())
    End Sub

    Private Sub listen()
        assert_begin(New callbackAction(check:=
                            Function() As Boolean
                                If stopping OrElse Not active() Then
                                    Return True
                                Else
                                    While listener.Pending() AndAlso
                                          connectionCount() < maxConnectionCount
                                        Dim ctor As _do(Of TcpClient, handle) = Nothing
                                        If async Then
                                            ctor = Function(ByRef x) New async_handle(Me, x)
                                        Else
                                            ctor = Function(ByRef x) New sync_handle(Me, x)
                                        End If
                                        add_handle(ctor(listener.AcceptTcpClient()))
                                    End While
                                    Return False
                                End If
                            End Function))
    End Sub

    Public Sub [stop]()
        stopping = True
        socketTrans.close(listener.Server)
        listener.Stop()
        Dim waitTime As UInt32 = 30
        While connectionCount() > 0 AndAlso waitTime > 0
            sleepSeconds(1)
            waitTime -= 1
        End While
        GC.KeepAlive(listener)
    End Sub

    Protected Overrides Sub Finalize()
        [stop]()
        MyBase.Finalize()
    End Sub

    Private Function raise_sync(ByVal input() As Byte, ByRef output() As Byte) As Boolean
        Dim o() As Byte = Nothing
        Return do_(Sub(ByRef r As Boolean)
                       RaiseEvent receive_sync(input, o, r)
                   End Sub,
                   False) AndAlso
               eva(output, o)
    End Function

    Private Function raise_async(ByVal input() As Byte, ByVal output As pointer(Of bytes)) As eventComb
        Return eventComb.return(Sub(ByRef ec As eventComb)
                                    RaiseEvent receive_async(input, output, ec)
                                End Sub)
    End Function

    Private MustInherit Class handle
        Protected ReadOnly client As tcp_client
        Protected ReadOnly server As tcp_pac_server(Of ASYNC_OPERATION)
        Protected MustOverride Function handle() As eventComb

        Protected Sub New(ByVal server As tcp_pac_server(Of ASYNC_OPERATION), ByVal client As TcpClient)
            assert(Not client Is Nothing)
            assert(Not server Is Nothing)
            Me.client = New tcp_client(client)
            Me.server = server
            Dim hec As eventComb = Nothing
            assert_begin(New eventComb(Function() As Boolean
                                           hec = Nothing
                                           hec = handle()
                                           assert(Not hec Is Nothing)
                                           Return waitfor(hec) AndAlso goto_next()
                                       End Function,
                                       Function() As Boolean
                                           If hec.end_result() AndAlso Not server.stopping Then
                                               Return goto_prev()
                                           Else
                                               Return goto_next()
                                           End If
                                       End Function,
                                       Function() As Boolean
                                           server.remove_handle(Me)
                                           Return goto_end()
                                       End Function))
        End Sub
    End Class

    Private Class async_handle
        Inherits handle

        Public Sub New(ByVal server As tcp_pac_server(Of ASYNC_OPERATION), ByVal client As TcpClient)
            MyBase.New(server, client)
            assert(istype(Of tcp_pac_server(Of _true))(server))
        End Sub

        Protected Overrides Function handle() As eventComb
            Dim ec As eventComb = Nothing
            client.reply(Function(ByRef input() As Byte, ByRef output As pointer(Of bytes)) As eventComb
                             Return server.raise_async(input, output)
                         End Function,
                         ec)
            Return ec
        End Function
    End Class

    Private Class sync_handle
        Inherits handle

        Public Sub New(ByVal server As tcp_pac_server(Of ASYNC_OPERATION), ByVal client As TcpClient)
            MyBase.New(server, client)
            assert(istype(Of tcp_pac_server(Of _false))(server))
        End Sub

        Protected Overrides Function handle() As eventComb
            Dim ec As eventComb = Nothing
            client.reply(Function(ByRef input() As Byte, ByRef output() As Byte) As Boolean
                             Return server.raise_sync(input, output)
                         End Function,
                         ec)
            Return ec
        End Function
    End Class
End Class
